#pragma once

#include "Maths.h"

using namespace maths;

CMatrix4x4f& CMatrix4x4f::InvertTransform()
{
	m_Data[ 3] = -(m_Data[12] * m_Data[0] + m_Data[13] * m_Data[1] + m_Data[14] * m_Data[ 2]);
	m_Data[ 7] = -(m_Data[12] * m_Data[4] + m_Data[13] * m_Data[5] + m_Data[14] * m_Data[ 6]);
	m_Data[11] = -(m_Data[12] * m_Data[8] + m_Data[13] * m_Data[9] + m_Data[14] * m_Data[10]);

	m_Data[12] = m_Data[13] = m_Data[14] = 0.0f;

	Transpose();

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakeTransform(const CVector4f& a_Pos, const CVector4f& a_Dir, const CVector4f& a_Up)
{
	Left() = a_Up ^ a_Dir;
	Up  () = a_Dir ^ Left();
	Dir () = a_Dir;
	Pos () = a_Pos;

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakeRotateTransformDeg(const CVector4f& a_Dir, const mFloat a_Angle)
{
	CVector4f Dir = a_Dir;
	Dir.Normalize();
	mFloat Sin = sin(a_Angle * PI_180), Cos = cos(a_Angle * PI_180);

	m_Data[ 0] = Cos + (1.0f - Cos)* Dir[0] * Dir[0];
	m_Data[ 1] = (1.0f - Cos)* Dir[1] * Dir[0]- Sin * Dir[2];
	m_Data[ 2] = (1.0f - Cos) * Dir[2] * Dir[0] + Sin * Dir[1];
	m_Data[ 3] = 0.0f;

	m_Data[ 4] = (1.0f - Cos) * Dir[0] * Dir[1] + Sin * Dir[2] ;
	m_Data[ 5] = Cos + (1.0f - Cos) * Dir[1] * Dir[1];
	m_Data[ 6] = (1.0f - Cos) * Dir[2] * Dir[1] - Sin * Dir[0];
	m_Data[ 7] = 0.0f;

	m_Data[ 8] = (1.0f - Cos) * Dir[0] * Dir[2] - Sin * Dir[1] ;
	m_Data[ 9] = (1.0f - Cos) * Dir[1] * Dir[2] + Sin * Dir[0];
	m_Data[10] = Cos + (1.0f - Cos) * Dir[2] * Dir[2];
	m_Data[11];

	m_Data[12] = 0.0f;
	m_Data[13] = 0.0f;
	m_Data[14] = 0.0f;
	m_Data[15] = 1.0f;

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakeRotateTransformRad(const CVector4f& a_Dir, const mFloat a_Angle)
{
	CVector4f Dir = a_Dir;
	Dir.Normalize();
	mFloat Sin = sin(a_Angle), Cos = cos(a_Angle);

	m_Data[ 0] = Cos + (1.0f - Cos)* Dir[0] * Dir[0];
	m_Data[ 1] = (1.0f - Cos)* Dir[1] * Dir[0]- Sin * Dir[2];
	m_Data[ 2] = (1.0f - Cos) * Dir[2] * Dir[0] + Sin * Dir[1];
	m_Data[ 3] = 0.0f;

	m_Data[ 4] = (1.0f - Cos) * Dir[0] * Dir[1] + Sin * Dir[2] ;
	m_Data[ 5] = Cos + (1.0f - Cos) * Dir[1] * Dir[1];
	m_Data[ 6] = (1.0f - Cos) * Dir[2] * Dir[1] - Sin * Dir[0];
	m_Data[ 7] = 0.0f;

	m_Data[ 8] = (1.0f - Cos) * Dir[0] * Dir[2] - Sin * Dir[1] ;
	m_Data[ 9] = (1.0f - Cos) * Dir[1] * Dir[2] + Sin * Dir[0];
	m_Data[10] = Cos + (1.0f - Cos) * Dir[2] * Dir[2];
	m_Data[11];

	m_Data[12] = 0.0f;
	m_Data[13] = 0.0f;
	m_Data[14] = 0.0f;
	m_Data[15] = 1.0f;

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakeScaleTransform(const mFloat a_X, const mFloat a_Y, const mFloat a_Z)
{
	m_Data[ 0] = a_X; m_Data[ 1] = 0.0; m_Data[ 2] = 0.0; m_Data[ 3] = 0.0;
	m_Data[ 4] = 0.0; m_Data[ 5] = a_Y; m_Data[ 6] = 0.0; m_Data[ 7] = 0.0;
	m_Data[ 8] = 0.0; m_Data[ 9] = 0.0; m_Data[10] = a_Z; m_Data[11] = 0.0;
	m_Data[12] = 0.0; m_Data[13] = 0.0; m_Data[14] = 0.0; m_Data[15] = 1.0;

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakeOrtho(const CVector4f& a_Min, const CVector4f& a_Max)
{
	m_Data[ 0] = 2.0f / (a_Max[0] - a_Min[0]); 
	m_Data[ 4] = 0.0f;
	m_Data[ 8] = 0.0f;
	m_Data[12] = - (a_Max[0] + a_Min[0]) / (a_Max[0] - a_Min[0]);

	m_Data[ 1] = 0.0f;
	m_Data[ 5] = 2.0f / (a_Max[1] - a_Min[1]); 
	m_Data[ 9] = 0.0f;
	m_Data[13] = - (a_Max[1] + a_Min[1]) / (a_Max[1] - a_Min[1]);

	m_Data[ 2] = 0.0f;
	m_Data[ 6] = 0.0f;
	m_Data[10] = 1.0f / (a_Max[3] - a_Min[3]);
	m_Data[14] = - a_Min[3] / (a_Max[3] - a_Min[3]);

	m_Data[ 3] = 0.0f;
	m_Data[ 9] = 0.0f;
	m_Data[11] = 0.0f;
	m_Data[15] = 1.0f;

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakePerspectiveDeg(const mFloat a_FovY, const mFloat a_Aspect, const mFloat a_ZNear, const mFloat a_ZFar)
{
	float l_Ytan = tan(a_FovY * PI_180 * 0.5f);
	float l_Xtan = l_Ytan * a_Aspect;

	m_Data[ 0] = 1.0f / l_Xtan; m_Data[ 1] =          0.0f; m_Data[ 2] =                                    0.0f; m_Data[ 3] = 0.0f;
	m_Data[ 4] =          0.0f; m_Data[ 5] = 1.0f / l_Ytan; m_Data[ 6] =                                    0.0f; m_Data[ 7] = 0.0f;
	m_Data[ 8] =          0.0f; m_Data[ 9] =          0.0f; m_Data[10] =             a_ZFar / (a_ZFar - a_ZNear); m_Data[11] = 1.0f;
	m_Data[12] =          0.0f; m_Data[13] =          0.0f; m_Data[14] = - a_ZNear * a_ZFar / (a_ZFar - a_ZNear); m_Data[15] = 0.0f;

	return *this;
}

CMatrix4x4f& CMatrix4x4f::MakePerspectiveRad(const mFloat a_FovY, const mFloat a_Aspect, const mFloat a_ZNear, const mFloat a_ZFar)
{
	float l_Ytan = tan(a_FovY * 0.5f);
	float l_Xtan = l_Ytan * a_Aspect;

	m_Data[ 0] = 1.0f / l_Xtan; m_Data[ 1] =          0.0f; m_Data[ 2] =                                    0.0f; m_Data[ 3] = 0.0f;
	m_Data[ 4] =          0.0f; m_Data[ 5] = 1.0f / l_Ytan; m_Data[ 6] =                                    0.0f; m_Data[ 7] = 0.0f;
	m_Data[ 8] =          0.0f; m_Data[ 9] =          0.0f; m_Data[10] =             a_ZFar / (a_ZFar - a_ZNear); m_Data[11] = 1.0f;
	m_Data[12] =          0.0f; m_Data[13] =          0.0f; m_Data[14] = - a_ZNear * a_ZFar / (a_ZFar - a_ZNear); m_Data[15] = 0.0f;

	return *this;
}

CVector4f& CMatrix4x4f::Pos()
{
	return *(CVector4f*)(&m_Data[12]);
}

CVector4f& CMatrix4x4f::Dir()
{
	return *(CVector4f*)(&m_Data[ 8]);
}

CVector4f& CMatrix4x4f::Up()
{
	return *(CVector4f*)(&m_Data[ 4]);
}

CVector4f& CMatrix4x4f::Left()
{
	return *(CVector4f*)(&m_Data[ 0]);
}